package main

import (
	"dnet-dl/config"
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"strings"
	"time"

	"github.com/go-redis/redis"
)

// 初始值
var (
	// redis客户端
	redisClient *redis.Client

	// http服务
	httpServer *http.Server

	// domain协议、domain
	domainProtocol = "https"
	domain         = "d.sosotec.com"

	// 字符种子
	asciis = []rune{
		'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
		'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
		'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
	}

	// redis键配置
	redisKeys = map[string]string{
		"mapping": "dl_url_mapping",
	}
)

// 连接redis
func connectRedis() {
	cRedis := config.Redis()
	redisClient = redis.NewClient(&redis.Options{
		Addr:     cRedis.Host + ":" + cRedis.Port,
		Password: cRedis.Password,
		DB:       cRedis.DB,
	})
	_, err := redisClient.Ping().Result()
	if err != nil {
		log.Fatal(err)
	}
}

// 生成短链key
func buildShortKey() string {
	// 生成随机数种子
	rd := rand.New(rand.NewSource(time.Now().UnixNano()))

	// 生成（需要检查是否存在重复的key，最多检查32次）
	var shortKey string
	for i := 0; i < 32; i += 1 {
		shortBuffer := make([]rune, 12)
		asciisLen := len(asciis)
		for k := range shortBuffer {
			asciisIndex := rd.Intn(asciisLen)
			shortBuffer[k] = rune(asciis[asciisIndex])
		}
		tmpShortKey := string(shortBuffer)

		// 检查redis是否存在key
		val, err := redisClient.HExists(redisKeys["mapping"], tmpShortKey).Result()
		if err == nil && !val {
			shortKey = tmpShortKey
			break
		}
	}

	// 返回
	return shortKey
}

// 获取长连接
func getLongUrl(shortKey string) string {
	// 参数检查
	if shortKey == "" {
		return ""
	}

	// 从redis中取出
	longUrl, err := redisClient.HGet(redisKeys["mapping"], shortKey).Result()
	if err != nil {
		return ""
	}

	// 返回
	return longUrl
}

// 启动http服务
func startHttpServe() *http.Server {
	// 泛指
	http.HandleFunc("/", func(write http.ResponseWriter, request *http.Request) {
		// 限制请求方式
		if request.Method != "GET" {
			return
		}

		// 此处高并发请求，需要启动协程处理
		hold := make(chan bool)
		go func() {
			// 解析出短链key
			shortKey := strings.Trim(request.RequestURI, "/")

			// 取出映射的长链接
			longUrl := getLongUrl(shortKey)
			if longUrl == "" {
				hold <- true
				return
			}

			fmt.Println(longUrl)

			// 设置请求头
			write.Header().Set("Location", longUrl)

			// 301跳转
			write.WriteHeader(301)

			// 释放通道
			hold <- true
		}()
		<-hold
	})

	// 录入短链接
	http.HandleFunc("/save", func(write http.ResponseWriter, request *http.Request) {
		// 限制请求方式
		if request.Method != "POST" {
			return
		}

		// 提取参数
		shortKey := request.PostFormValue("shortKey")
		longUrl := request.PostFormValue("longUrl")
		password := request.PostFormValue("password")

		// 检查操作密码
		if config.Http().Password != password {
			write.Write([]byte("抱歉，操作密码不正确！"))
			return
		}

		// 如果短链不为空，生成一个
		if shortKey == "" {
			shortKey = buildShortKey()
		}
		if shortKey == "" {
			return
		}

		// 写入（不检查返回值，因为旧值被覆盖会返回false）
		_, err := redisClient.HSet(redisKeys["mapping"], shortKey, longUrl).Result()
		if err != nil {
			return
		}

		// 输出
		write.Write([]byte(domainProtocol + "://" + domain + "/" + shortKey))
	})

	// 查看映射列表
	http.HandleFunc("/list", func(write http.ResponseWriter, request *http.Request) {
		// 限制请求方式
		if request.Method != "GET" {
			return
		}

		//从redis的hset中取出所有内容
		all, err := redisClient.HGetAll(redisKeys["mapping"]).Result()
		if err != nil {
			return
		}
		buffer, _ := json.Marshal(all)

		// 输出
		write.Write(buffer)
	})

	// 启动服务
	cHttp := config.Http()
	httpServer = &http.Server{
		Addr: cHttp.Host + ":" + cHttp.Port,

		Handler: http.DefaultServeMux,
	}
	if err := httpServer.ListenAndServe(); err != nil {
		log.Fatal(err)
	}

	//返回实例
	return httpServer
}

func main() {
	// 连接redis
	connectRedis()
	defer redisClient.Close()

	// 启动http服务
	startHttpServe()
	defer httpServer.Close()
}
